﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Ninject;
using Reap.Common.Runtime.Dependency;
using NinjectParameters = Ninject.Parameters;

namespace Reap.Common.Runtime.Dependency.Ninject
{
	/// <summary>
	/// 容器封装类，减少其他引用程序对Ninject的依赖
	/// </summary>
	public class ContainerManager : StandardKernel, IContainerManager
	{
		#region Fields

		private IKernel _container;

		#endregion

		#region .ctor

		public ContainerManager()
		{
			_container = new StandardKernel();
		}

		#endregion

		#region Properties

		public IKernel Container
		{
			get { return _container; }
		}

		#endregion

		#region AddComponent

		public void AddComponent<TService>(string key = "", ComponentLifeStyle lifeStyle = ComponentLifeStyle.Singleton)
		{
			AddComponent(typeof (TService), typeof (TService), key, lifeStyle);
		}

		public void AddComponent(Type type, string key = "", ComponentLifeStyle lifeStyle = ComponentLifeStyle.Singleton)
		{
			AddComponent(type, type, key, lifeStyle);
		}

		public void AddComponent<TService, TImplementation>(string key = "",
			ComponentLifeStyle lifeStyle = ComponentLifeStyle.Singleton)
		{
			AddComponent(typeof (TService), typeof (TImplementation), key, lifeStyle);
		}

		public void AddComponent(Type service, Type implementation, string key = "",
			ComponentLifeStyle lifeStyle = ComponentLifeStyle.Singleton, params Parameter[] parameters)
		{
			var binding = _container.Bind(service).To(implementation);
			if (parameters != null)
			{
				var ninjectParameter = ConvertParameters(parameters);
				foreach (var parameter in ninjectParameter)
				{
					binding.WithParameter(parameter);
				}
			}
			binding.PerLifeStyle(lifeStyle).MapKey(key).ReplaceExisting(service);
		}

		#endregion

		#region AddComponentInstance

		public void AddComponentInstance<TService>(object instance, string key = "")
		{
			AddComponentInstance(typeof (TService), instance, key);
		}

		public void AddComponentInstance(object instance, string key = "")
		{
			AddComponentInstance(instance.GetType(), instance, key);
		}

		public void AddComponentInstance(Type service, object instance, string key = "")
		{
			_container.Bind(service).ToConstant(instance).MapKey(key).ReplaceExisting(service);
		}

		#endregion

		#region ConvertParameters

		private static NinjectParameters.IParameter[] ConvertParameters(Parameter[] parameters)
		{
			if (parameters == null)
			{
				return null;
			}
			return
				parameters.Select(it => new NinjectParameters.ConstructorArgument(it.Name, (context) => it.ValueCallback()))
					.ToArray();
		}

		#endregion

		#region Resolve

		public virtual T Resolve<T>(string key = "", params Parameter[] parameters) where T : class
		{
			if (string.IsNullOrWhiteSpace(key))
			{
				return _container.Get<T>(ConvertParameters(parameters));
			}
			return _container.Get<T>(key, ConvertParameters(parameters));
		}

		public virtual object Resolve(Type type, string key = "", params Common.Runtime.Parameter[] parameters)
		{
			if (string.IsNullOrWhiteSpace(key))
			{
				return _container.Get(type, ConvertParameters(parameters));
			}
			return _container.Get(type, key, ConvertParameters(parameters));
		}

		#endregion

		#region ResolveAll

		public virtual T[] ResolveAll<T>(string key = "")
		{
			if (string.IsNullOrWhiteSpace(key))
			{
				return _container.GetAll<T>().ToArray();
			}
			return _container.GetAll<T>(key).ToArray();
		}

		public virtual object[] ResolveAll(Type type, string key = "")
		{
			if (string.IsNullOrWhiteSpace(key))
			{
				return _container.GetAll(type).ToArray();
			}
			return _container.GetAll(type, key).ToArray();
		}

		#endregion

		#region TryResolve

		public virtual T TryResolve<T>(string key = "", params Common.Runtime.Parameter[] parameters)
		{
			if (string.IsNullOrWhiteSpace(key))
			{
				return _container.TryGet<T>(ConvertParameters(parameters));
			}
			return _container.TryGet<T>(key, ConvertParameters(parameters));
		}


		public virtual object TryResolve(Type type, string key = "", params Common.Runtime.Parameter[] parameters)
		{
			if (string.IsNullOrWhiteSpace(key))
			{
				return _container.TryGet(type, ConvertParameters(parameters));
			}
			return _container.TryGet(type, key, ConvertParameters(parameters));
		}

		#endregion

		#region ResolveUnRegistered

		public T ResolveUnregistered<T>() where T : class
		{
			return ResolveUnregistered(typeof (T)) as T;
		}

		public object ResolveUnregistered(Type type)
		{
			var constructors = type.GetConstructors();
			foreach (var constructor in constructors)
			{
				var parameters = constructor.GetParameters();
				var parameterInstances = new List<object>();
				foreach (var parameter in parameters)
				{
					var service = Resolve(parameter.ParameterType);
					if (service != null)
						parameterInstances.Add(service);
				}
				return Activator.CreateInstance(type, parameterInstances.ToArray());
			}
			throw new Exception("构造方法没有发现需要依赖的字段");
		}

		#endregion

		public new void Dispose()
		{
			if (this._container != null && !this._container.IsDisposed)
			{
				this._container.Dispose();
			}
			this._container = null;
		}
	}
}